using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DesignerSupport;
using System.Reflection;
using System.ComponentModel;
using System.Xml;

namespace XMTAEditor
{
	public static class MetadataFactory
	{
		public static NetCFMetadata FromXml(string xmtaFilePath)
		{
			System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(NetCFMetadata));
			using (XmlReader rdr = XmlReader.Create(System.IO.File.Open(xmtaFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)))
				return (NetCFMetadata)ser.Deserialize(rdr);
		}

		public static NetCFMetadata FromAssembly(string assemblyPath)
		{
			Assembly asm = Assembly.LoadFrom(assemblyPath);
			NetCFMetadata xmta = new NetCFMetadata();
			try
			{
				SortedList<string, ClassType> Classes = new SortedList<string, ClassType>();
				foreach (Type t in asm.GetTypes())
				{
					if (t.IsClass && t.IsPublic && t.IsVisible && t.IsSubclassOf(typeof(System.ComponentModel.Component)))
					{
						if (Classes.ContainsKey(t.FullName))
							continue;
						ClassType ctype = new ClassType(t.FullName);
						Classes.Add(ctype.Name, ctype);
						SortedList<string, PropertyType> Properties = new SortedList<string, PropertyType>();
						foreach (PropertyInfo pi in t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
						{
							if (Properties.ContainsKey(pi.Name))
								continue;
							PropertyType ptype = new PropertyType(pi.Name);
							ptype.DefaultValue = new objecttype(pi.PropertyType);
							ptype.AmbientValue = new objecttype(pi.PropertyType);
							Properties.Add(ptype.Name, ptype);
						}
						ctype.Property = new List<PropertyType>(Properties.Values);
						SortedList<string, EventType> Events = new SortedList<string, EventType>();
						foreach (EventInfo ei in t.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
						{
							if (Events.ContainsKey(ei.Name))
								continue;
							EventType etype = new EventType(ei.Name);
							Events.Add(etype.Name, etype);
						}
						ctype.Event = new List<EventType>(Events.Values);
						SortedList<string, MethodType> Methods = new SortedList<string, MethodType>();
						foreach (MethodInfo mi in t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
						{
							if (mi.IsSpecialName) continue;
							if (Methods.ContainsKey(mi.Name))
								continue;
							MethodType mtype = new MethodType(mi.Name);
							Methods.Add(mtype.Name, mtype);
						}
						ctype.Method = new List<MethodType>(Methods.Values);
					}
				}
				xmta.Class = new List<ClassType>(Classes.Values);
			}
			catch (ReflectionTypeLoadException)
			{
				AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Form1.CurrentDomain_AssemblyResolve);
				return null;
			}
			return xmta;
		}

		public static NetCFMetadata Merge(NetCFMetadata primary, NetCFMetadata secondary)
		{
			NetCFMetadata md = null;
			if (primary != null && secondary != null)
			{
				md = new NetCFMetadata();
				List<ClassType> Classes = new List<ClassType>(primary.Class);
				foreach (ClassType xct in secondary.Class)
				{
					ClassType ctype = Classes.Find(delegate(ClassType ct1) { return string.Compare(ct1.Name, xct.Name, true) == 0; });
					if (ctype == null)
					{
						Classes.Add(xct);
					}
					else
					{
						if (ctype.Property == null)
							ctype.Property = xct.Property;
						else if (xct.Property != null)
						{
							List<PropertyType> Properties = new List<PropertyType>(ctype.Property);
							foreach (PropertyType xpt in xct.Property)
							{
								PropertyType ptype = Properties.Find(delegate(PropertyType pt1) { return string.Compare(pt1.Name, xpt.Name, true) == 0; });
								if (ptype == null)
									Properties.Add(xpt);
								else
								{
									if (ptype.DefaultValue == null)
										ptype.DefaultValue = xpt.DefaultValue;
									else if (xpt.DefaultValue != null)
										ptype.DefaultValue.InternalType = xpt.DefaultValue.InternalType;
									if (ptype.AmbientValue == null)
										ptype.AmbientValue = xpt.AmbientValue;
									else if (xpt.AmbientValue != null)
										ptype.AmbientValue.InternalType = xpt.AmbientValue.InternalType;
								}
							}
							//Properties.Sort(delegate(PropertyType p1, PropertyType p2) { return p1.Name.CompareTo(p2.Name); });
							ctype.Property = Properties;
						}
						if (ctype.Event == null)
							ctype.Event = xct.Event;
						else if (xct.Event != null)
						{
							List<EventType> Events = new List<EventType>(ctype.Event);
							foreach (EventType xet in xct.Event)
							{
								EventType etype = Events.Find(delegate(EventType et1) { return string.Compare(et1.Name, xet.Name, true) == 0; });
								if (etype == null)
								{
									Events.Add(xet);
								}
							}
							//Events.Sort(delegate(EventType p1, EventType p2) { return p1.Name.CompareTo(p2.Name); });
							ctype.Event = Events;
						}
						if (ctype.Method == null)
							ctype.Method = xct.Method;
						else if (xct.Method != null)
						{
							List<MethodType> Methods = new List<MethodType>(ctype.Method);
							foreach (MethodType xmt in xct.Method)
							{
								MethodType mtype = Methods.Find(delegate(MethodType mt1) { return string.Compare(mt1.Name, xmt.Name, true) == 0; });
								if (mtype == null)
								{
									mtype = xmt;
									Methods.Add(xmt);
								}
							}
							//Methods.Sort(delegate(MethodType p1, MethodType p2) { return p1.Name.CompareTo(p2.Name); });
							ctype.Method = Methods;
						}
					}
				}

				Classes.Sort(delegate(ClassType p1, ClassType p2) { return p1.Name.CompareTo(p2.Name); });
				md.Class = Classes;
			}
			else
			{
				if (primary == null)
					md = secondary;
				else
					md = primary;
			}
			return md;
		}

		public static void Save(NetCFMetadata metadata, string filename)
		{
			// Clean up empty items
			if (metadata.Class != null)
			{
				for (int i = metadata.Class.Count - 1; i >= 0; i--)
				{
					bool hasSubs = false;
					ClassType ct = metadata.Class[i];
					for (int j = ct.Event.Count - 1; j >= 0; j--)
					{
						if (!ct.Event[j].HasData())
							ct.Event.RemoveAt(j);
						else
							hasSubs = true;
					}
					for (int j = ct.Property.Count - 1; j >= 0; j--)
					{
						if (!ct.Property[j].HasData())
							ct.Property.RemoveAt(j);
						else
							hasSubs = true;
					}
					for (int j = ct.Method.Count - 1; j >= 0; j--)
					{
						if (!ct.Method[j].HasData())
							ct.Method.RemoveAt(j);
						else
							hasSubs = true;
					}
					if (!ct.HasData() && !hasSubs)
						metadata.Class.RemoveAt(i);
				}
			}
			// Save to xml stream
			System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(NetCFMetadata));
			System.Xml.XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			System.IO.File.Delete(filename);
			using (XmlWriter wri = XmlWriter.Create(filename, settings))
			{
				ser.Serialize(wri, metadata);
				wri.Flush();
			}
			/*System.IO.File.Delete(filename);
			metadata.SaveFile(filename);*/
		}
	}
}
